using System;
using System.Collections.Generic;
using System.Text;
using WozLib.UI.Ink.SketchCommands;
using System.Xml.Serialization;
using System.Windows.Ink;
using System.Windows;
using WozLib.WozCommands;
using WozLib.WozObjects;
using WozLib.UI.Ink.SketchObjects;
using WozLib.UI.Ink;

namespace WOzTestUI.WozCommands
{
  public class WozCommitChangesCommand : WozObjectWozCommand
  {
    // This is assigned when a REDO happens
    [XmlAttribute("WozClientClearCommand")]
    public ClearSketchCommand WozClientClearCommand
    {
      get { return _WozClientClearCommand; }
      set { _WozClientClearCommand = value; }
    } private ClearSketchCommand _WozClientClearCommand;

    // This is assigned when a REDO happens
    [XmlAttribute("WozWindowResizeCommand")]
    public WindowResizeWozCommand WozWindowResizeCommand
    {
      get { return _WozWindowResizeCommand; }
      set { _WozWindowResizeCommand = value; }
    } private WindowResizeWozCommand _WozWindowResizeCommand;

    // This is assigned when a REDO happens
    [XmlAttribute("WindowRenameSketchCommand")]
    public WindowRenameSketchCommand WindowRenameSketchCommand
    {
      get { return _WindowRenameSketchCommand; }
      set { _WindowRenameSketchCommand = value; }
    } private WindowRenameSketchCommand _WindowRenameSketchCommand;

    // This is assigned when a REDO happens
    [XmlAttribute("WindowRenameSketchCommand")]
    public ModifyUserCurrentInkTool ModifyUserCurrentInkTool
    {
      get { return _ModifyUserCurrentInkTool; }
      set { _ModifyUserCurrentInkTool = value; }
    } private ModifyUserCurrentInkTool _ModifyUserCurrentInkTool;

    // This is assigned when a REDO happens
    [XmlAttribute("ModifyUserToolsEnabledCollectionSketchCommand")]
    public ModifyUserToolsEnabledCollection ModifyUserToolsEnabledCollection
    {
      get { return _ModifyUserToolsEnabledCollection; }
      set { _ModifyUserToolsEnabledCollection = value; }
    } private ModifyUserToolsEnabledCollection _ModifyUserToolsEnabledCollection;

    // This is assigned when a REDO happens
    [XmlAttribute("PreviousCommittedScreenServerID")]
    public long PreviousCommittedScreenServerID
    {
      get
      {
        return _PreviousCommittedScreenServerID;
      }
      set
      {
        _PreviousCommittedScreenServerID = value;
      }
    } long _PreviousCommittedScreenServerID;

    // This is assigned when a REDO happens
    [XmlAttribute("PreviousCommittedScreenInOverlay")]
    public bool PreviousCommittedScreenInOverlay
    {
      get
      {
        return _PreviousCommittedScreenInOverlay;
      }
      set
      {
        _PreviousCommittedScreenInOverlay = value;
      }
    } bool _PreviousCommittedScreenInOverlay;

    // This is assigned when a REDO happens
    [XmlElement("SinceCommitServerCommandStack")]
    public Stack<WozLib.WozCommands.WozCommand> SinceCommitServerCommandStack
    {
      get
      {
        return _SinceCommitServerCommandStack;
      }
      set
      {
        _SinceCommitServerCommandStack = value;
      }
    } Stack<WozLib.WozCommands.WozCommand> _SinceCommitServerCommandStack;

    // This is assigned when a REDO happens
    [XmlAttribute("ClientChangeCommandQueue")]
    public Queue<SketchCommandCollection> ClientChangeCommandQueue
    {
      get
      {
        return _ClientChangeCommandQueue;
      }
      set
      {
        _ClientChangeCommandQueue = value;
      }
    } Queue<SketchCommandCollection> _ClientChangeCommandQueue;

    /// <summary>
    /// Returns the WizardWindow that is the target of this command. Returns null if none exists.
    /// </summary>
    [XmlIgnore]
    public WizardWindow TargetWizardWindow
    {
      get
      {
        IServerObject iServerObject = TargetIServerObject;
        if (iServerObject is WizardWindow)
        {
          return iServerObject as WizardWindow;
        }
        else
        {
          return null;
        }
      }
      set
      {
        TargetServerID = value.ServerID;
      }
    }

    public WozCommitChangesCommand()
    {
    }

    public WozCommitChangesCommand(WizardWindow wizardWindow)
      : base (wizardWindow)
    {
    }

    // an UNDO has to happen first so that we have a SinceCommitServerCommandStack and a ClientChangeCommandQueue to use for UNDO
    public override void Undo()
    {
      WizardWindow wizardWindow = TargetWizardWindow;

      if (wizardWindow != null &&
        wizardWindow.ClientCommittedScreen != null &&
        wizardWindow.ScreensListBox != null &&
        wizardWindow.ClientChangeCommandQueue != null &&
        SinceCommitServerCommandStack != null && ClientChangeCommandQueue != null)
      {

        if (wizardWindow.ClientCommittedScreen != null)
        {
          
          // take the selected screen out of overlay mode
          // if for some reason it was really supposed to be we'll fix that later
          // do this first when UNDOing
          bool inOverlaymode = false;

          // send an UNDO of the clear command
          // need to use the same Ref from the REDO

          //WozClientClearCommand wozClientClearCommand = new WozClientClearCommand();
          wizardWindow.SendClientCommand(new UndoSketchCommands(WozClientClearCommand));

          // restore the state of the SinceCommitServerCommandStack
          wizardWindow.ClientCommittedScreen.SinceCommitServerCommandStack = SinceCommitServerCommandStack;

          // get a ref to the previous clientCommitedScreen
          IServerObject iServerObject = ServerObjectRegistry.GetObject(PreviousCommittedScreenServerID);

          // check that the IServerObject is in fact a WozObjectContainer
          if (iServerObject is WozScreen)
          {
            WozScreen ws = iServerObject as WozScreen;

            // restore the ref to the previous clientCommitedScreen
            wizardWindow.ClientCommittedScreen = ws;

            // set the InOverlayMode for clientCommitedScreen
            inOverlaymode = PreviousCommittedScreenInOverlay;
          }

          // restore the state of change log
          wizardWindow.ClientChangeCommandQueue = ClientChangeCommandQueue;

          //restore the window size
          wizardWindow.SendClientCommand(new UndoSketchCommands(WozWindowResizeCommand));

          //restore the window name
          wizardWindow.SendClientCommand(new UndoSketchCommands(WindowRenameSketchCommand));

          // restore what tools are available
          wizardWindow.SendClientCommand(new UndoSketchCommands(ModifyUserToolsEnabledCollection));

          //restore the edit mode
          wizardWindow.SendClientCommand(new UndoSketchCommands(ModifyUserCurrentInkTool));

          // we have to do this last because it will override the current editing mode
          wizardWindow.ClientCommittedScreen.InOverlayMode = inOverlaymode;
        }

      }
    }

    public override void Redo()
    {
      WizardWindow wizardWindow = TargetWizardWindow;

      if (wizardWindow != null &&
        wizardWindow.ScreensListBox != null &&
        wizardWindow.ClientChangeCommandQueue != null)
      {
        if (wizardWindow.ScreensListBox.SelectedItem != null &&
          wizardWindow.ScreensListBox.SelectedItem is WozScreen)
        {
          // save who the previous committed screen was, even if it is us
          // we'll need to possibly put it back into overlay mode
          // and make it the currentclientcommittedscreen
          PreviousCommittedScreenServerID = wizardWindow.ClientCommittedScreen.ServerID;
          PreviousCommittedScreenInOverlay = wizardWindow.ClientCommittedScreen.InOverlayMode;

          if (wizardWindow.ClientCommittedScreen !=
            wizardWindow.ScreensListBox.SelectedItem)
          {
            // take the old screen out of ink committed mode
            wizardWindow.ClientCommittedScreen.InOverlayMode = false;

            // update reference
            wizardWindow.ClientCommittedScreen =
              wizardWindow.ScreensListBox.SelectedItem as WozScreen;
          }

          // save state of change log
          ClientChangeCommandQueue = wizardWindow.ClientChangeCommandQueue;

          // clear the change log after a commit
          wizardWindow.ClientChangeCommandQueue = new Queue<SketchCommandCollection>();
        }

        if (wizardWindow.ClientCommittedScreen != null)
        {
          // push the selected screen's ClientObjectRegistry over to the client

          // send clear command
          // need to only create this once and use the same Ref in UNDO
          WozClientClearCommand = new ClearSketchCommand(true);
          wizardWindow.SendClientCommand(WozClientClearCommand);

          // send create command for everything visible
          List<SketchObject> toAdd = new List<SketchObject>();

          foreach (UIElement uie in wizardWindow.ClientCommittedScreen.Children)
          {
            if (uie is ISketchObject)
            {
              toAdd.Add((uie as ISketchObject).GetWozObject());
            }
          }

          foreach (Stroke stroke in wizardWindow.ClientCommittedScreen.Strokes)
          {
            if (stroke is ISketchObject)
            {
              toAdd.Add((stroke as ISketchObject).GetWozObject());
            }
          }

          CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(toAdd);
          wizardWindow.SendClientCommand(wozClientCreateCommand);

          // for every selected object send a selection command
          List<SketchObject> nextSelectedWozObjects = new List<SketchObject>();

          // make sure only "selectable" things get selected
          foreach (UIElement uie in wizardWindow.ClientCommittedScreen.GetSelectedElements())
          {
            if (uie is ISketchObject)
            {
              nextSelectedWozObjects.Add((uie as ISketchObject).GetWozObject());
            }
          }
          foreach (Stroke stroke in wizardWindow.ClientCommittedScreen.GetSelectedStrokes())
          {
            if (stroke is ISketchObject)
            {
              nextSelectedWozObjects.Add((stroke as ISketchObject).GetWozObject());
            }
          }

          ChangeSelectionSketchCommand wcsc = new ChangeSelectionSketchCommand(nextSelectedWozObjects, new List<SketchObject>());
          wizardWindow.SendClientCommand(wcsc);

          // save the state of the SinceCommitServerCommandStack
          SinceCommitServerCommandStack = wizardWindow.ClientCommittedScreen.SinceCommitServerCommandStack;

          // clear out the ClientCommittedScreen's SinceCommitServerCommandStack
          wizardWindow.ClientCommittedScreen.SinceCommitServerCommandStack = new Stack<WozCommand>();

          // put the selected screen into clientoverlay mode
          // do this first when UNDOing
          wizardWindow.ClientCommittedScreen.InOverlayMode = true;

          // resize the client window
          WozWindowResizeCommand = new WindowResizeWozCommand(
            new Size(wizardWindow.ClientCommittedScreen.Width,
            wizardWindow.ClientCommittedScreen.Height));
          wizardWindow.SendClientCommand(WozWindowResizeCommand);

          //rename the window
          WindowRenameSketchCommand = new WindowRenameSketchCommand(wizardWindow.ClientCommittedScreen.Description);
          wizardWindow.SendClientCommand(WindowRenameSketchCommand);

          //set what tools are available
          ModifyUserToolsEnabledCollection = new ModifyUserToolsEnabledCollection(wizardWindow.ClientCommittedScreen.UserToolsEnabledCollection);
          wizardWindow.SendClientCommand(ModifyUserToolsEnabledCollection);

          //set the right client selection mode
          ModifyUserCurrentInkTool = new ModifyUserCurrentInkTool(wizardWindow.ClientCommittedScreen.UserCurrentInkTool);
          wizardWindow.SendClientCommand(ModifyUserCurrentInkTool);
        }
      }
    }
  }
}
